const PENDING = 'pending';//进行中
const FULFILLED = 'fulfilled';//完成态
const REJECTED = 'rejected';//拒绝或者说失败态
//Promise Resolution Procedure [[Resolve]](promise2, x)
//Promise解决过程是一个抽象操作，接受一个promise和一个值作为输入
//如果x是一个thenable，它尝试使promise采用x的状态
//假设x至少在某种程度上像一个promise。否则，它使用值x来实现promise。
function resolutionProcedure(promise2, x, resolve, reject) {
    //2.3.1 如果promise和x引用同一个对象，则以TypeError为原因拒绝promise。
    if (promise2 === x) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
    }
    //2.3.2 如果x是一个promise，采用其状态[3.4]：
    //2.3.2.1 如果x处于待定状态，则promise必须保持待定状态，直到x被实现或拒绝。
    //2.3.2.2 如果/当x被实现时，用相同的值实现promise。
    //2.3.2.3 如果/当x被拒绝时，用相同的原因拒绝promise。
    //2.3.3 否则，如果x是一个对象或函数：
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
        //2.3.3.1 让then为x.then
        //2.3.3.3.3如果resolvePromise和rejectPromise都被调用，或者对同一个参数进行多次调用，
        //则第一次调用优先，任何后续调用都将被忽略。
        let called = false;
        try {
            //2.3.3.2 如果获取属性x .then导致抛出异常e，则以e为原因拒绝promise。
            let then = x.then;
            //2.3.3.3如果then是一个函数，则以x作为this，
            //第一个参数为resolvePromise，第二个参数为rejectPromise调用它，其中：
            if (typeof then === 'function') {
                //2.3.3.3.1 当resolvePromise被调用并传入值y，运行[[Resolve]](promise, y)。
                //y就是x成功的值,因为y也可能是一个Promise,所以再进行递归处理
                then.call(
                    x, y => {
                        if(called)return;
                        called=true;
                        resolutionProcedure(promise2, y, resolve, reject)
                    },
                    //2.3.3.3.2如果/当rejectPromise被调用并传入原因r，以r拒绝promise。
                    r=>{
                        if(called)return;
                        called=true;
                        reject(r)
                    });//x.then(resolve,reject)
            }else{
                //2.3.3.4 如果then不是一个函数，则以x来实现promise。
                resolve(x);
            }
        } catch (e) {
            //2.3.3.3.4 如果调用then导致抛出异常e，
            //2.3.3.3.4.1如果已经调用了resolvePromise或rejectPromise，则忽略它。
            if(called)return;
            called=true;
            //2.3.3.3.4.2否则，以e为原因拒绝promise。
            return reject(e);
        }
    } else {
        //2.3.4 如果x不是对象或函数，则用x来实现promise。
        resolve(x);
    }
}
class Promise<T> {
    state = PENDING //当刚创建Promise的时候初始状态是pending
    value //存放成功的值，这也是一个实例属性
    reason//存放失败的原因，这也是一个实例属性
    //2.2.6 then方法可以在同一个promise上多次调用。
    onResolvedCallbacks = []
    onRejectedCallbacks = []
    constructor(executor) {
        //调用resolve可以传递成功的值，并且可以把状态改为FULFILLED
        const resolve = (value: T) => {
            if(value instanceof Promise){
                return value.then(resolve,reject)
            }
            //2.1.3 处于rejected状态的promise：不能转换为其他任何状态。
            //2.2.2.3 它不能被调用多次。
            if (this.state === PENDING) {
                this.state = FULFILLED;//状态变成成功态
                //2.1.2.2 必须具有一个值，该值不得更改。
                this.value = value;//值为成功的结果
                //当调用resolve方法成功了就执行成功的回调
                //2.2.2.1 它必须在promise实现后调用，并以promise的值作为其第一个参数。
                //2.2.6.1 如果当promise被实现时，所有相应的onFulfilled回调函数必须按照它们发起then调用的顺序执行。
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }
        //调用reject可以传递失败的原因，并且可以把状态改为REJECTED
        const reject = (reason) => {
            //2.1.2 处于fulfilled状态的promise：不能转换为其他任何状态。
            //2.2.3.3 它不能被调用多次。
            if (this.state === PENDING) {
                this.state = REJECTED;//状态变成失败态
                //2.1.3.2 必须具有一个原因，该原因不得更改。
                this.reason = reason;//记录失败的原因
                //当调用reject方法失败了就调用失败的回调
                //2.2.3.1 它必须在promise被拒绝后调用，并以promise的原因作为其第一个参数。
                //2.2.3.2 在promise被拒绝之前不得调用它。
                //2.2.6.2 如果当promise被拒绝时，所有相应的onRejected回调函数必须按照它们发起then调用的顺序执行。
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        //传递resolve和reject并执行executor函数
        executor(resolve, reject);
    }
    settle(callback, value, promise2, resolve, reject) {
        try {
            //2.2.5 onFulfilled和onRejected必须作为函数被调用（即没有this值）
            const x = callback(value);//value+'a'
            //2.2.7.1 如果onFulfilled或onRejected返回一个值x，
            //则运行Promise Resolution Procedure [[Resolve]](promise2, x)。
            resolutionProcedure(promise2, x, resolve, reject)
        } catch (e) {
            //2.2.7.2如果onFulfilled或onRejected抛出异常e，则promise2必须以e作为原因被拒绝。
            reject(e);
        }

    }
    //promise必须提供一个then方法以访问其当前或最终的值或原因。
    //指定成功和失败的回调
    //一个promise的then方法接受两个参数：
    //2.2.1 onFulfilled和onRejected都是可选参数
    then(
        onfulfilled?: (value: any) => any,
        onrejected?: (reason: any) => any): Promise<any> {
        //2.2.3 then方法必须返回一个promise
        let promise2 = new Promise((resolve, reject) => {
            //2.2.1.1 如果onFulfilled不是一个函数，它必须被忽略。
            if (!onfulfilled || typeof onfulfilled !== 'function') {
                //2.2.7.3如果onFulfilled不是一个函数且promise1被实现，则promise2必须以与promise1相同的值被实现。
                onfulfilled = value => value;
            }
            //2.2.1.2 如果onRejected不是一个函数，它必须被忽略。
            if (!onrejected || typeof onrejected !== 'function') {
                //2.2.7.4 如果onRejected不是一个函数且promise1被拒绝，则promise2必须以与promise1相同的原因被拒绝。
                onrejected = reason => { throw reason };
            }
            //2.2.2 如果onFulfilled是一个函数：
            //2.2.3 如果onRejected是一个函数：
            //如果调用then方法的时候状态是成功态
            if (this.state === FULFILLED) {
                //直接马上调用成功的回调并且传递成成功的值
                //2.2.2.1 它必须在promise实现后调用，并以promise的值作为其第一个参数。
                //2.2.2.2 在promise实现之前不得调用它。
                //2.2.4. onFulfilled或onRejected不能在执行上下文堆栈中只包含平台代码之前调用。[ 3.1]。
                queueMicrotask(() => {
                    this.settle(onfulfilled, this.value, promise2, resolve, reject);
                });
            }
            //如果调用then的时候状态是失败态
            if (this.state === REJECTED) {
                //直接马上调用失败的回调函数并且传递失败的原因
                //2.2.3.1 它必须在promise被拒绝后调用，并以promise的原因作为其第一个参数。
                queueMicrotask(() => {
                    this.settle(onrejected, this.reason, promise2, resolve, reject);
                });
            }
            //如果调用then的时候状态是进行中
            if (this.state === PENDING) {
                //缓存成功和失败的回调
                //2.2.2.1 它必须在promise实现后调用，并以promise的值作为其第一个参数。
                this.onResolvedCallbacks.push(() => {
                    queueMicrotask(() => {
                        this.settle(onfulfilled, this.value, promise2, resolve, reject);
                    });
                });
                //2.2.3.1 它必须在promise被拒绝后调用，并以promise的原因作为其第一个参数。
                this.onRejectedCallbacks.push(() => {
                    queueMicrotask(() => {
                        this.settle(onrejected, this.reason, promise2, resolve, reject);
                    });
                });
            }
        });
        //2.2.3 then方法必须返回一个promise
        return promise2;
    }
    catch(onrejected){
        return this.then(undefined,onrejected);
    }
    finally(callback){
        return this.then(callback,callback);
    }
    static resolve(value){
        return new Promise((resolve,reject)=>{
            resolve(value)
        });
    }
    static reject(reason){
        return new Promise((resolve,reject)=>{
            reject(reason)
        });
    }
    static all(promises){
        return new Promise((resolve,reject)=>{
            //定义一个数组用来存放每个promise成功的值
            let results =[];
            //已经完成的promise的数量
            let completed = 0;
            promises.forEach((promise,index)=>{
                Promise.resolve(promise).then(value=>{
                    results[index]=value;
                    completed++;
                    if(completed>=promises.length){
                        resolve(results);
                    }
                },reason=>{
                    reject(reason);
                });
            });
        });
    }
    static race(promises){
        return new Promise((resolve,reject)=>{
            promises.forEach((promise)=>{
                Promise.resolve(promise).then(resolve,reject);
            });
        });
    }
    static any(promises){
        return new Promise((resolve,reject)=>{
            let errors=[];
            let completed=0;
            promises.forEach((promise,index)=>{
                Promise.resolve(promise).then(resolve,(reason)=>{
                    errors[index]=reason;
                    completed++;
                    if(completed === promises.length){
                        reject(
                            new AggregateError(
                                errors,
                                'All promises were rejected'
                            )
                        );
                    }
                });
            });
        });
    }
    static allSettled(promises){
        return new Promise((resolve,reject)=>{
            let results=[];
            let completed=0;
            promises.forEach((promise,index)=>{
                Promise.resolve(promise).then((value)=>{
                    results[index]={ status: 'fulfilled', reason: value };
                    completed++;
                    if(completed === promises.length){
                       resolve(results)
                    }
                },(reason)=>{
                    results[index]={ status: 'rejected', reason };
                    completed++;
                    if(completed === promises.length){
                       resolve(results)
                    }
                });
            });
        });
    }
}
(Promise as any).deferred = function () {
    let dfd:any = {};
    dfd.promise = new Promise((resolve, reject) => {
      dfd.resolve = resolve;
      dfd.reject = reject;
    });
    return dfd;
  };
export default Promise;